package com.cloudfun.campusshare.service.impl;


import cn.hutool.core.bean.BeanUtil;
import com.cloudfun.campusshare.common.constant.AttachType;
import com.cloudfun.campusshare.common.constant.Code;
import com.cloudfun.campusshare.common.exception.BusinessException;
import com.cloudfun.campusshare.common.model.vo.AttachVO;
import com.cloudfun.campusshare.common.response.BaseResult;
import com.cloudfun.campusshare.common.response.BaseResultFactory;
import com.cloudfun.campusshare.entity.AttachmentEntity;
import com.cloudfun.campusshare.repo.AttachmentEntityRepo;
import com.cloudfun.campusshare.service.AttachmentService;
import com.cloudfun.campusshare.util.AliyunOSSUtil;
import com.cloudfun.campusshare.util.FileHashUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;


/**
 * @description: 附件相关
 * @author: linyangping
 * @create: 2019-11-19 15:20
 **/
@Service
@Slf4j
public class AttachmentServiceImpl implements AttachmentService {

    @Autowired
    private AttachmentEntityRepo attachmentEntityRepo;

    @Autowired
    private AliyunOSSUtil aliyunOSSUtil;

    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public BaseResult<AttachVO> uploadAttachment(AttachType type, MultipartFile file) {
        log.info("文件上传{}",type.name());
        AttachVO attachVO = null;
        try {
            // 获取文件名
            String fileName = file.getOriginalFilename();
            // 获取文件后缀
            String prefix = fileName.substring(fileName.lastIndexOf("."));
            // 获取文件内容hash值
            String fileHash = FileHashUtil.getFileHashByFile(file, "SHA-256");
            // 将文件上传到阿里云
            String objectName = fileHash + System.currentTimeMillis() + prefix;
            aliyunOSSUtil.saveFile(file, objectName);
            // 获取文件url地址
            String url = aliyunOSSUtil.getFileUrl(objectName);
            AttachmentEntity attachmentEntity = new AttachmentEntity();
            attachmentEntity.setName(fileName);
            attachmentEntity.setBucketName(aliyunOSSUtil.getDefaultBucketName());
            attachmentEntity.setFileHash(fileHash);
            attachmentEntity.setUrl(url);
            attachmentEntity.setCount(1);
            attachmentEntity.setType(type);
            attachmentEntity.setObjName(objectName);
            attachmentEntityRepo.save(attachmentEntity);
            attachVO = new AttachVO();
            BeanUtil.copyProperties(attachmentEntity, attachVO);
        } catch (Exception e) {
            log.error("文件上传失败{}",e.getMessage());
        }
        return BaseResultFactory.produceResult(Code.SUCCESS, attachVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void relateAttachment(String subjectId, List<String> attachIds, AttachType attachType) {
        try {
            if (StringUtils.isEmpty(subjectId) || CollectionUtils.isEmpty(attachIds)) {
                throw new BusinessException(Code.NODATA);
            }
            List<AttachmentEntity> newAttachEntities = attachmentEntityRepo.findAllById(attachIds);
            Optional<AttachmentEntity> errorTypeAttach = newAttachEntities.stream().filter(attachmentEntity -> !attachmentEntity.getType().equals(attachType)).findAny();
            if (errorTypeAttach.isPresent()) {
                throw new BusinessException(Code.ERROR, "附件类型错误");
            }
            List<AttachType> needUpdateTypes = newAttachEntities.stream().map(AttachmentEntity::getType).collect(Collectors.toList());
            // 删除旧的附件
            List<AttachmentEntity> oldEntities = attachmentEntityRepo.findAllBySubjectIdAndTypeIn(subjectId, needUpdateTypes);
            oldEntities = oldEntities.stream()
                    .filter(oldEntity -> !attachIds.contains(oldEntity.getId()))
                    .collect(Collectors.toList());
            this.deleteAttachment(oldEntities);
            // 关联新的附件
            List<AttachmentEntity> attachmentEntityList = newAttachEntities.stream()
                    .filter(newEntity -> !subjectId.equals(newEntity.getSubjectId()))
                    .collect(Collectors.toList());
            attachmentEntityList.forEach(attachmentEntity -> attachmentEntity.setSubjectId(subjectId));
            attachmentEntityRepo.saveAll(attachmentEntityList);
        } catch (BusinessException e) {
            log.error("文件关联出错{}",e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void relateAttachment(String subjectId, String attachId, AttachType attachType) {
        Optional<AttachmentEntity> attachOpt = attachmentEntityRepo.findById(attachId);
        if (!attachOpt.isPresent()) {
            throw new BusinessException(Code.NODATA);
        }
        AttachmentEntity attachmentEntity = attachOpt.get();
        if (!attachmentEntity.getType().equals(attachType)) {
            throw new BusinessException(Code.ERROR, "附件类型错误");
        }
        // 删除旧的附件
        List<AttachmentEntity> oldEntities = attachmentEntityRepo.findAllBySubjectIdAndType(subjectId, attachmentEntity.getType());
        oldEntities = oldEntities.stream()
                .filter(oldEntity -> !oldEntity.getId().equals(attachId))
                .collect(Collectors.toList());
        this.deleteAttachment(oldEntities);
        // 关联新的附件
        if (!subjectId.equals(attachmentEntity.getSubjectId())) {
            attachmentEntity.setSubjectId(subjectId);
            attachmentEntityRepo.save(attachmentEntity);
        }
    }

    @Override
    public List<AttachVO> getAttachment(String subjectId) {
        List<AttachmentEntity> attachmentEntityList = attachmentEntityRepo.findAllBySubjectId(subjectId);
        return attachmentEntityList.stream()
                .map(attachmentEntity -> {
                    AttachVO attachVO = new AttachVO();
                    BeanUtil.copyProperties(attachmentEntity, attachVO);
                    return attachVO;
                }).collect(Collectors.toList());
    }

    @Override
    public List<AttachVO> getAttachment(String subjectId, List<AttachType> types) {
        List<AttachmentEntity> attachmentEntityList = attachmentEntityRepo.findAllBySubjectIdAndTypeIn(subjectId, types);
        return attachmentEntityList.stream()
                .map(attachmentEntity -> {
                    AttachVO attachVO = new AttachVO();
                    BeanUtil.copyProperties(attachmentEntity, attachVO);
                    return attachVO;
                }).collect(Collectors.toList());
    }

    @Override
    public List<AttachVO> getAttachment(String subjectId, AttachType type) {
        List<AttachmentEntity> attachmentEntityList = attachmentEntityRepo.findAllBySubjectIdAndType(subjectId, type);
        return attachmentEntityList.stream()
                .map(attachmentEntity -> {
                    AttachVO attachVO = new AttachVO();
                    BeanUtil.copyProperties(attachmentEntity, attachVO);
                    return attachVO;
                }).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAttachment(String subjectId, AttachType type) {
        List<AttachmentEntity> attachListInDB = attachmentEntityRepo.findAllBySubjectIdAndType(subjectId, type);
        this.deleteAttachment(attachListInDB);
    }

    @Override
    public void deleteAttachment(String subjectId, List<AttachType> types) {
        List<AttachmentEntity> attachListInDB = attachmentEntityRepo.findAllBySubjectIdAndTypeIn(subjectId, types);
        this.deleteAttachment(attachListInDB);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAttachment(List<AttachmentEntity> attachmentEntities) {
        // 删除阿里云上的文件
        attachmentEntities.forEach(attachmentEntity -> {
            aliyunOSSUtil.deleteFile(attachmentEntity.getObjName());
        });
        attachmentEntityRepo.deleteAll(attachmentEntities);
    }

    @Override
    public void deleteAttachmentEntity(String subjectId, List<AttachType> types) {
        List<AttachmentEntity> attachListInDB = attachmentEntityRepo.findAllBySubjectIdAndTypeIn(subjectId, types);
        this.deleteAttachmentEntity(attachListInDB);
    }

    @Override
    public void deleteAttachmentEntity(List<AttachmentEntity> attachmentEntities) {
        attachmentEntityRepo.deleteAll(attachmentEntities);
    }

    @Override
    @Transactional
    public void copyAttachment(List<String> attachIds, String dstSubjectId) {
        List<AttachmentEntity> attachmentEntities = attachmentEntityRepo.findAllById(attachIds);
        List<AttachmentEntity> attachmentEntityList = attachmentEntities.stream().map(srcEntity -> {
            AttachmentEntity dstEntity = new AttachmentEntity();
            BeanUtil.copyProperties(srcEntity, dstEntity);
            dstEntity.setId(null);
            dstEntity.setSubjectId(dstSubjectId);
            return dstEntity;
        }).collect(Collectors.toList());
        attachmentEntityRepo.saveAll(attachmentEntityList);
    }

    /**
     * 获取附件
     *
     * @param id
     * @return
     */
    @Override
    public AttachmentEntity getAttach(String id) {
        return attachmentEntityRepo.findById(id).orElse(null);
    }
}
